﻿#ifndef MovableObject_h
#define MovableObject_h

#include "AnimatedSprite.h"
#include "GameItem.h"
#include "QRectangle.h"
#include "StaticObject.h"
#include "Score.h"

//movable
//check collision
class MovableObject : public GameItem
{
public:
	MovableObject() : GameItem()
	{
		_isMovable = true;
		jumping = false;
		_vy = 0;
		_countTimeDisappear = 0;
	}
	MovableObject(int id, D3DXVECTOR2 position, int MovingWidth) : GameItem(id, position, MovingWidth)
	{
		_isMovable = true;
		_vy = 0;
		_addScore = false;
		_score = 100;
	}
	~MovableObject(){}

	D3DXVECTOR2 GetVectology()
	{
		return _vectology;
	}

	virtual void Update(int Delta, vector<GameObject*> vectorObj, QRectangle bound)
	{	
		//fall
		_vy -= 0.3f;

		//limit _vy
		if (_vy > _speedY) _vy = _speedY;
		if (_vy < -_speedY) _vy = -_speedY;

		// Limit the sideways acceleration
		if (_vx > _speedX) _vx = _speedX;
		if (_vx < -_speedX) _vx = -_speedX;

		float minX = 1, minY = 1, normalXResult = 0, normalYResult = 0;// collision time min
		float minY2 = 1, normalYResult2 = 0; // for mario vs movable obj
		int indexX = -1, indexY = -1;
		int indexY2 = -1;

		for(size_t j = 0; j < vectorObj.size(); j++)
		{	
			//check movable obj (for mario only)
			if(this -> GetType() == MarioPlayer && vectorObj.at(j) -> isMovableObj() && ((MovableObject*)vectorObj.at(j))-> _state == normal)
			{
				//0: Xét y, 1: Xét x
				for(int dir=0;dir<2;++dir)
				{
					if(dir == 0)
					{
						_vectology.x = 0;
						_vectology.y = (float)((this->_vy - ((MovableObject*) vectorObj.at(j))->GetVectology().y) * Delta);

						QRectangle broadphasebox = GetSweptBroadphaseBox(this);
						QRectangle staticBox =  QRectangle(vectorObj.at(j) -> GetLeft(), vectorObj.at(j) -> GetLowY(),
							(float)vectorObj.at(j) -> GetWidth(), (float)vectorObj.at(j) -> GetHeight());

						if(broadphasebox.Intersect(staticBox))
						{
							float normalX, normalY;
							float collisionTime = SweptAABB(this, (StaticObject*)vectorObj.at(j), normalX, normalY);

							if(normalX == 0 && collisionTime < minY2)
							{
								minY2 = collisionTime;
								indexY2 = j;
								normalYResult2 = normalY;
							}
						}
					}
					else // Xét theo x
					{
						_vectology.x = (float)((this->_vx - ((MovableObject*) vectorObj.at(j))->GetVectology().x) * Delta);
						_vectology.y = 0;

						QRectangle broadphasebox = GetSweptBroadphaseBox(this);
						QRectangle staticBox =  QRectangle(vectorObj.at(j) -> GetLeft(), vectorObj.at(j) -> GetLowY(),
							(float)vectorObj.at(j) -> GetWidth(), (float)vectorObj.at(j) -> GetHeight());

						if(broadphasebox.Intersect(staticBox))
						{
							float normalX, normalY;
							float collisionTime = SweptAABB(this, (StaticObject*)vectorObj.at(j), normalX, normalY);

							if(normalY == 0 && collisionTime < minX)
							{
								minX = collisionTime;
								indexX = j;
								normalXResult = normalX;
							}
						}
					}
				}
			}

			//check not movable obj (static obj)
			if(this!=vectorObj.at(j) && !vectorObj.at(j) -> isMovableObj() && ((MovableObject*)vectorObj.at(j))-> _state == normal)
			{
				//0: Xét y, 1: Xét x
				for(int dir=0;dir<2;++dir)
				{
					if(dir == 0)
					{
						_vectology.x = 0;
						_vectology.y = (float)(_vy * Delta);
						//Lấy khung của thằng chuyển động
						QRectangle broadphasebox = GetSweptBroadphaseBox(this);
						//lấy khung của thằng đứng yên
						QRectangle staticBox =  QRectangle(vectorObj.at(j) -> GetLeft(), vectorObj.at(j) -> GetLowY(),
							(float)vectorObj.at(j) -> GetWidth(), (float)vectorObj.at(j) -> GetHeight());

						//nếu 2 khung đụng thì xét va chạm
						if(broadphasebox.Intersect(staticBox))
						{
							float normalX, normalY;
							float collisionTime = SweptAABB(this, (StaticObject*)vectorObj.at(j), normalX, normalY);

							if(normalX == 0 && collisionTime < minY)
							{
								minY = collisionTime;
								indexY = j;
								normalYResult = normalY;
							}
						}
					}
					else //dir = 1
					{
						_vectology.x = (float)(_vx * Delta);
						_vectology.y = 0;
						QRectangle broadphasebox = GetSweptBroadphaseBox(this);
						QRectangle staticBox =  QRectangle(vectorObj.at(j) -> GetLeft(), vectorObj.at(j) -> GetLowY(),
							(float)vectorObj.at(j) -> GetWidth(), (float)vectorObj.at(j) -> GetHeight());

						if(broadphasebox.Intersect(staticBox))
						{
							float normalX, normalY;
							float collisionTime = SweptAABB(this, (StaticObject*)vectorObj.at(j), normalX, normalY);

							if(normalY == 0 && collisionTime < minX)
							{
								minX = collisionTime;
								indexX = j;
								normalXResult = normalX;
							}
						}
					}
				}
			}
		}
		bool enableGravity = true; //another movable obj always has "enableGravity = true"
		if(indexY2!=-1)//indexY2 is index of another movable obj (not mario)
		{
			if(normalYResult2 == 1) //kill enemy
			{
				if(((MovableObject*)vectorObj.at(indexY2))->_state != hurt){

					((MovableObject*)vectorObj.at(indexY2))->_state = hurt;
					if(!((MovableObject*)vectorObj.at(indexY2))->_addScore)
					{
						Score::score += 1;
						((MovableObject*)vectorObj.at(indexY2))->_addScore = true;
					}
				}
			}
			else 
			{
				enableGravity = false;
				this -> _state = hurt;
			}

		}
		if(enableGravity && indexY!=-1)
		{
			_position.y += (float)(_vy * minY * Delta + 0.5f);
			_vy = 0;

			if(normalYResult == -1)
			{
				if (this -> GetType() == MarioPlayer)
					jumping = true;
				//Update _state for static obj
				if(((StaticObject*)vectorObj.at(indexY))->GetType()==BrickNormalType)
					((StaticObject*)vectorObj.at(indexY))->_state = hurt;
				if(((StaticObject*)vectorObj.at(indexY))->GetType()==BrickGoldType && !((StaticObject*)vectorObj.at(indexY))->_addScore)
				{
					((StaticObject*)vectorObj.at(indexY))->flagCall = true;
					Score::score += 10;
					((StaticObject*)vectorObj.at(indexY))->_addScore = true;
				}
			}
			else if(normalYResult == 1)
			{
				if (this -> GetType() == MarioPlayer)
					jumping = false;
			}

		}
		if(indexX!=-1 && indexY!=indexX)
		{
			_position.x += (float)(_vx * minX * Delta);
			
			//Dành cho những thằng chuyển động (trừ mario)
			if(this -> GetType() != MarioPlayer)
			{
				_vx = -_vx;
			}
			else _vx = 0;
		}

		//Nếu có va chạm thì _vx hoặc _vy = 0 => position vẫn giữ nguyên
		_position.x += (float)(_vx * Delta);
		_position.y += (float)(_vy * Delta);
	}

	float GetLeftStop()
	{
		return leftStop;
	}

	float GetRightStop()
	{
		return rightStop;
	}

protected:
	QRectangle GetSweptBroadphaseBox(MovableObject *b)
	{
		return QRectangle(
			(b -> GetVectology().x >= 0 ? b -> GetLeft() : b -> GetLeft() + b -> GetVectology().x),
			(b -> GetVectology().y >= 0 ? b -> GetLowY() : b -> GetLowY() + b -> GetVectology().y),
			(b -> GetVectology().x >= 0 ? b -> GetVectology().x + b -> GetWidth() : b -> GetWidth() - b -> GetVectology().x),
			(b -> GetVectology().y >= 0 ? b -> GetVectology().y + b -> GetHeight() : b -> GetHeight() - b -> GetVectology().y));
	}
	//return entry time of collision
	float SweptAABB(MovableObject* moveObject, StaticObject* staticObject, float& normalx, float& normaly)
	{
		//distance
		float xInvEntry, xInvExit;

		//distance
		float yInvEntry, yInvExit;

		if(moveObject -> GetVectology().x > 0.0f)
		{
			xInvEntry = staticObject -> GetLeft() - (moveObject -> GetLeft() + moveObject -> GetWidth());
			xInvExit = (staticObject -> GetLeft() + staticObject -> GetWidth()) - moveObject -> GetLeft();
		}
		else
		{
			xInvEntry = (staticObject -> GetLeft() + staticObject -> GetWidth()) - moveObject -> GetLeft();
			xInvExit = staticObject -> GetLeft() - (moveObject -> GetLeft() + moveObject -> GetWidth());
		}
		if(moveObject -> GetVectology().y > 0.0f)
		{
			yInvEntry = staticObject -> GetLowY() - (moveObject -> GetLowY() + moveObject -> GetHeight());
			yInvExit = (staticObject -> GetLowY() + staticObject -> GetHeight()) - moveObject -> GetLowY();
		}
		else
		{
			yInvEntry = (staticObject -> GetLowY() + staticObject -> GetHeight()) - moveObject -> GetLowY();
			yInvExit = staticObject -> GetLowY() - (moveObject -> GetLowY() + moveObject -> GetHeight());
		}

		// find time of collision and time of leaving for each axis
		float xEntry, xExit;
		float yEntry, yExit;

		if(moveObject -> GetVectology().x == 0.0f)
		{
			xEntry = - std::numeric_limits<float>::infinity();
			xExit = std::numeric_limits<float>::infinity();
		}
		else
		{
			xEntry = xInvEntry / moveObject -> GetVectology().x;
			xExit = xInvExit / moveObject -> GetVectology().x;
		}

		if (moveObject -> GetVectology().y == 0.0f)
		{
			yEntry = -std::numeric_limits<float>::infinity();
			yExit = std::numeric_limits<float>::infinity();
		}
		else
		{
			yEntry = yInvEntry / moveObject-> GetVectology().y;
			yExit = yInvExit / moveObject-> GetVectology().y;
		}

		// find the earliest/latest times of collision
		float entryTime = (std::max)(xEntry, yEntry);
		float exitTime = (std::min)(xExit, yExit);

		// if there was no collision
		if (entryTime > exitTime || (xEntry < 0.0f && yEntry < 0.0f) || xEntry > 1.0f || yEntry > 1.0f)
		{
			normalx = 0.0f;
			normaly = 0.0f;
			return 1.0f;
		}
		else // if there was a collision
		{  
			// calculate normal of collided surface
			//touch y-axis first
			if (xEntry > yEntry)
			{
				if (xInvEntry > 0.0f)
				{
					normalx = -1.0f;
					normaly = 0.0f;
				}
				else
				{
					normalx = 1.0f;
					normaly = 0.0f;
				}
			} 
			else //touch x-axis first
				//that means moveObject collides with upper or bottom side of staticObject.
				//if vy == 0 then yEntry = - infinity, then xEntry > yEntry, not in this case.
			{
				if (yInvEntry < 0.0f)
				{
					normalx = 0.0f;
					normaly = 1.0f;
				}
				else
				{
					normalx = 0.0f;
					normaly = -1.0f;
				}
			}

			// return the time of collision
			return entryTime;
		}
	}
	float leftStop, rightStop;
	bool jumping;
	AnimatedSprite *_moveSprite;

	float _speedX, _speedY;

	// velocity of object (1s)
	float _vx;		
	float _vy;

	//the distance object moves every update()
	D3DXVECTOR2 _vectology;

	//this is to control the animate rate of object
	DWORD last_time;

	int _countTimeDisappear;

	int _score;
};

#endif